﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Linq.Expressions;
using System.Reflection.Emit;
using System.IO;

namespace Tmo
{
	public class ControllerDispatcher<TController>
	{
		TController Controller;
		public ControllerDispatcher(TController controller)
		{
			Controller = controller;
		}

		public void Init()
		{
#if DEBUG
			var timer = new System.Diagnostics.Stopwatch();
			timer.Start();
#endif
			var methods = typeof(TController).GetMethods(BindingFlags.Instance | BindingFlags.Public)
				.Where(m => m.IsVirtual);

			var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
				new AssemblyName(string.Format("{0}", "ABC" + Guid.NewGuid().ToString("N"))),
					AssemblyBuilderAccess.RunAndSave);

			var module = assembly.DefineDynamicModule("DispatcherModule");

			var type = module.DefineType(typeof(TController).Name + "Dispatcher", TypeAttributes.Public | TypeAttributes.Class | TypeAttributes.Serializable, typeof(TController));

			foreach (var method in methods)
			{
				var overrideMethod = type.DefineMethod(method.Name, method.Attributes, method.CallingConvention, method.ReturnType,
												method.GetParameters().Select(pi => pi.ParameterType).ToArray());

				BuildOverrideMethod(overrideMethod, method);

				type.DefineMethodOverride(overrideMethod, method);

			}

			type.DefineDefaultConstructor(MethodAttributes.Public);

			type.CreateType();
			assembly.Save("Test.dll");

#if DEBUG
			timer.Stop();
			System.Diagnostics.Debug.WriteLine("Init took {0:0.000} sec", timer.Elapsed.TotalSeconds);
#endif
		}

		private void BuildOverrideMethod(MethodBuilder builder, MethodInfo baseMethod)
		{
			var ilgenerator = builder.GetILGenerator();
			ilgenerator.Emit(OpCodes.Ldarg_0);
			ilgenerator.EmitCall(OpCodes.Callvirt, baseMethod, null);
			ilgenerator.Emit(OpCodes.Ret);
			//Expression<Func<object>> getself = () => this;
		}
	}
}
